import { BorderType } from "./borderType";
import { getX, getY, avgAndClamp } from "./internal/utils";

/**
 * 对 Uint8ClampedArray 应用盒式滤波
 * @param data 输入像素数据 (RGBA 格式)
 * @param width 图像宽度
 * @param height 图像高度
 * @param radius 滤波半径
 * @param borderType 边界处理方式
 * @returns 滤波后的像素数据
 */
export function boxFilterUint8(
  data: Uint8ClampedArray,
  width: number,
  height: number,
  radius: number,
  borderType: BorderType = BorderType.REPLICATE,
): Uint8ClampedArray {
  if (radius < 0) {
    throw new Error("Radius must be non-negative");
  }

  if (radius === 0) {
    // 快速返回副本
    return new Uint8ClampedArray(data);
  }

  const size = width * height * 4;
  if (data.length !== size) {
    throw new Error("Data length does not match width, height and RGBA format");
  }

  // 使用单一临时缓冲区减少内存分配
  const temp = new Uint8ClampedArray(new ArrayBuffer(size));

  // 先水平滤波，再垂直滤波
  horizontalPass(data, temp, width, height, radius, borderType);
  verticalPass(temp, data, width, height, radius, borderType);

  // 确保返回值是普通的 Uint8ClampedArray
  if (data.buffer instanceof SharedArrayBuffer) {
    return new Uint8ClampedArray(
      new Uint8Array(data.buffer).buffer, // 将 SharedArrayBuffer 转换为普通 ArrayBuffer
    );
  }

  return data;
}

/**
 * 水平方向滤波（优化版本）
 */
function horizontalPass(
  src: Uint8ClampedArray,
  dest: Uint8ClampedArray,
  width: number,
  height: number,
  radius: number,
  borderType: BorderType,
): void {
  const stride = 4; // RGBA
  const rowSize = width * stride;

  for (let y = 0; y < height; y++) {
    const rowStart = y * rowSize;
    let sumR = 0,
      sumG = 0,
      sumB = 0;
    let count = 0;

    // 初始化第一个窗口
    for (let kx = -radius; kx <= radius; kx++) {
      const cx = getX(kx, width, borderType);
      if (cx === -1) continue;

      const pos = rowStart + cx * stride;
      sumR += src[pos];
      sumG += src[pos + 1];
      sumB += src[pos + 2];
      count++;
    }

    // 处理每一列
    for (let x = 0; x < width; x++) {
      const pos = rowStart + x * stride;
      dest[pos] = avgAndClamp(sumR, count);
      dest[pos + 1] = avgAndClamp(sumG, count);
      dest[pos + 2] = avgAndClamp(sumB, count);
      dest[pos + 3] = src[pos + 3];

      // 更新滑动窗口
      const leftX = x - radius - 1;
      const rightX = x + radius;
      const leftCX = getX(leftX, width, borderType);
      const rightCX = getX(rightX, width, borderType);

      if (leftCX !== -1) {
        const leftPos = rowStart + leftCX * stride;
        sumR -= src[leftPos];
        sumG -= src[leftPos + 1];
        sumB -= src[leftPos + 2];
        count--;
      }

      if (rightCX !== -1) {
        const rightPos = rowStart + rightCX * stride;
        sumR += src[rightPos];
        sumG += src[rightPos + 1];
        sumB += src[rightPos + 2];
        count++;
      }
    }
  }
}

/**
 * 垂直方向滤波（优化版本）
 */
function verticalPass(
  src: Uint8ClampedArray,
  dest: Uint8ClampedArray,
  width: number,
  height: number,
  radius: number,
  borderType: BorderType,
): void {
  const stride = 4; // RGBA
  const rowSize = width * stride;

  for (let x = 0; x < width; x++) {
    const xOffset = x * stride;
    let sumR = 0,
      sumG = 0,
      sumB = 0;
    let count = 0;

    // 初始化第一个窗口
    for (let ky = -radius; ky <= radius; ky++) {
      const cy = getY(ky, height, borderType);
      if (cy === -1) continue;

      const pos = cy * rowSize + xOffset;
      sumR += src[pos];
      sumG += src[pos + 1];
      sumB += src[pos + 2];
      count++;
    }

    // 处理每一行
    for (let y = 0; y < height; y++) {
      const pos = y * rowSize + xOffset;
      dest[pos] = avgAndClamp(sumR, count);
      dest[pos + 1] = avgAndClamp(sumG, count);
      dest[pos + 2] = avgAndClamp(sumB, count);
      dest[pos + 3] = src[pos + 3];

      // 更新滑动窗口
      const topY = y - radius - 1;
      const bottomY = y + radius;
      const topCY = getY(topY, height, borderType);
      const bottomCY = getY(bottomY, height, borderType);

      if (topCY !== -1) {
        const topPos = topCY * rowSize + xOffset;
        sumR -= src[topPos];
        sumG -= src[topPos + 1];
        sumB -= src[topPos + 2];
        count--;
      }

      if (bottomCY !== -1) {
        const bottomPos = bottomCY * rowSize + xOffset;
        sumR += src[bottomPos];
        sumG += src[bottomPos + 1];
        sumB += src[bottomPos + 2];
        count++;
      }
    }
  }
}
